package org.axeidls.api;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import org.axe.annotation.ioc.Autowired;
import org.axe.annotation.ioc.Controller;
import org.axe.annotation.ioc.Value;
import org.axe.annotation.mvc.Request;
import org.axe.annotation.mvc.RequestParam;
import org.axe.bean.mvc.HttpResponsePack;
import org.axe.bean.mvc.Param;
import org.axe.constant.RequestMethod;
import org.axe.exception.LogableException;
import org.axe.exception.RestException;
import org.axe.extra.timer.TimerTask;
import org.axe.home.rest.AxeRest;
import org.axe.util.CollectionUtil;
import org.axe.util.JsonUtil;
import org.axe.util.LogUtil;
import org.axeidls.component.DataFileComponent;
import org.axeidls.vo.DataBlockInfoPlus;
import org.axeidls.vo.DataRecordPackage;
import org.axeidls.vo.DataRecordPlus;
import org.axeidls.vo.DeviceDataListByTimestamp2;
import org.axeidls.vo.Page;
import org.axeidls.vo.StorageStatusPlus;

@Controller(name = "通用接口")
public class Api extends TimerTask{

	@Autowired
	private DataFileComponent dfc;
	@Autowired
	private AxeRest axeRest;

	public Api() {}
	
	public Api(DataFileComponent dfc, AxeRest axeRest) {
		this.dfc = dfc;
		this.axeRest = axeRest;
	}

	@Request(path = "get_device_path_all",method = RequestMethod.GET,name = "查询服务区全部设备路径集合")
	public Set<String> get_device_path_all(){
		return dfc.getDevicePathAll();
	}

	@Request(path = "download/*",method = RequestMethod.GET,name = "下载")
	public void download(Param param,HttpResponsePack response){
		String filePath = param.getRequestPath();
		filePath = filePath.substring(filePath.indexOf("download/")+"download/".length());
		while(filePath.startsWith("/")) {
			filePath = filePath.substring(1);
		}
		File dataFile = dfc.getDataFile(filePath);
		if(dataFile == null || !dataFile.exists()) {
			throw new RestException(404,"文件不存在");
		}
		filePath = filePath.replaceAll("/", "_")+".data";
		axeRest.outputFile(response, dataFile.getAbsolutePath(),filePath);
	}

	@Request(path = "get_free_space",method = RequestMethod.GET,name = "查询存储剩余可用容量，单位字节")
	public String get_free_space(){
		return String.valueOf(dfc.getFreeSpace());
	}

	@Request(path = "get_total_storage_status",method = RequestMethod.GET,name = "查询总的存储情况")
	public StorageStatusPlus get_total_storage_status(){
		try {
			StorageStatusPlus totalStorageStatus = dfc.getTotalStorageStatus();
			totalStorageStatus.setFreeSpace(dfc.getFreeSpace());
			return totalStorageStatus;
		} catch (Exception e) {
			throw new RestException("文件读取错误：{}",e.getMessage());
		}
	}

	@Request(path = "get_storage_status",method = RequestMethod.POST,name = "查询设备存储情况",desc = "避免参数超长，用POST")
	public StorageStatusPlus get_storage_status(
			@RequestParam(name = "devicePathAry",required = false,desc = "设备路径数组，一般是：系统/设备id")String[] devicePathAry
			){
		try {
			if(devicePathAry == null) {
				devicePathAry = new String[0];
			}
			StorageStatusPlus storageStatus = dfc.getStorageStatus(devicePathAry);
			storageStatus.setFreeSpace(dfc.getFreeSpace());
			return storageStatus;
		} catch (Exception e) {
			throw new RestException("文件读取错误：{}",e.getMessage());
		}
	}
	
	@Request(path = "list_data_file_path",method = RequestMethod.GET,name = "查询数据目录")
	public List<Map<String,Object>> list_data_file_path(
			@RequestParam(name = "dataPath",required = false,desc = "相对路径")String dataPath
			){
		return dfc.listDataFilePath(dataPath);
	}
	
	@Request(path = "list_data_year",method = RequestMethod.GET,name = "查询设备数据日期")
	public List<String> list_data_year(
			@RequestParam(name = "devicePath",required = true,desc = "设备路径，一般是：系统/设备id")String devicePath
			){
		return dfc.listDataYearFolder(devicePath);
	}

	@Request(path = "list_data_month",method = RequestMethod.GET,name = "查询设备数据日期")
	public List<String> list_data_month(
			@RequestParam(name = "devicePath",required = true,desc = "设备路径，一般是：系统/设备id")String devicePath,
			@RequestParam(name = "dataYear",required = true,desc = "数据年份",compile = "^[0-9]{4}$")String dataYear
			){
		return dfc.listDataMonthFolder(devicePath,dataYear);
	}

	@Request(path = "list_data_day",method = RequestMethod.GET,name = "查询设备数据日期")
	public List<String> list_data_day(
			@RequestParam(name = "devicePath",required = true,desc = "设备路径，一般是：系统/设备id")String devicePath,
			@RequestParam(name = "dataYear",required = true,desc = "数据年份（四位数字）",compile = "^[0-9]{4}$")String dataYear,
			@RequestParam(name = "dataMonth",required = true,desc = "数据月份（两位数字）",compile = "^[0-9]{2}$")String dataMonth
			){
		return dfc.listDataDayFolder(devicePath,dataYear,dataMonth);
	}
	
	@Request(path = "list_data_block_info",method = RequestMethod.GET,name = "查询设备数据块")
	public List<DataBlockInfoPlus> list_data_block_info(
			@RequestParam(name = "devicePath",required = true,desc = "设备路径，一般是：系统/设备id")String devicePath,
			@RequestParam(name = "dataYear",required = true,desc = "数据年份（四位数字）",compile = "^[0-9]{4}$")String dataYear,
			@RequestParam(name = "dataMonth",required = true,desc = "数据月份（两位数字）",compile = "^[0-9]{2}$")String dataMonth,
			@RequestParam(name = "dataDay",required = true,desc = "数据日（两位数字）",compile = "^[0-9]{2}$")String dataDay
			){
		return dfc.listDataBlockInfo(devicePath, dataYear, dataMonth, dataDay);
	}

	@Request(path = "get_by_timestamp",method = RequestMethod.GET,name = "根据时间戳，拉取一条横向记录")
	public Map<String,Map<String,String>> get_by_timestamp(
			@RequestParam(name = "timestamp",required = true,desc = "数据时间戳")Long timestamp,
			@RequestParam(name = "deviceColumnsArray",required = true,desc = "[\"设备1:列1,列2\",\"设备2:列1,列2\"]")String[] deviceColNamesArray
			){
		try {
			return dfc.getDevicesDataByTimestamp(timestamp,deviceColNamesArray);
		} catch (Exception e) {
			throw new RestException(e.getMessage());
		}
	}

	/**
	 * @param startTimeStr
	 * @param endTimeStr
	 * @param durTimeMs
	 * @param pageNum
	 * @param pageSize
	 * @param deviceColNamesArray
	 * @param dataTimestampFormat
	 * @return
	 * [
	 * 	{
	 * 		"timestamp":123123123,
	 * 		"colValues":[1,2,3,4]
	 *  }
	 * ]
	 */
	@Request(path = "list_value_by_time_zone",method = RequestMethod.GET,name = "根据时间范围，拉取报表",desc = "返回表格结构数据，横表结构，有限制一次最多10000条")
	public List<Map<String,Object>> list_value_by_time_zone(
			@RequestParam(name = "startTimestamp",required = true)Long startTimestamp,
			@RequestParam(name = "endTimestamp",required = true)Long endTimestamp,
			@RequestParam(name = "durTimeMs",required = true,desc = "时间间隔粒度，单位毫秒")Long durTimeMs,
			@RequestParam(name = "pageNum",required = true,desc = "页码，从1开始")Integer pageNum,
			@RequestParam(name = "pageSize",required = true,desc = "页长，最大1000")Integer pageSize,
			@RequestParam(name = "deviceColNamesArray",required = true,desc = "[\"设备1:列1,列2\",\"设备2:列1,列2\"]")String[] deviceColNamesArray,
			@RequestParam(name = "dataTimestampFormat",required = false,desc = "返回数据的时间戳格式化，默认不带格式化")String dataTimestampFormat
			){
		try {
			if(durTimeMs < 1) {
				durTimeMs = 1l;
			}
			if(pageNum < 1) {
				pageNum = 1;
			}
			if(pageSize < 1) {
				pageSize = 1;
			}
			if(pageSize > 10000) {
				throw new Exception("pageSize > 10000");
			}
			
			startTimestamp = startTimestamp+(pageNum-1)*pageSize*durTimeMs;
			long totalMs = (endTimestamp-startTimestamp)+1;
			int count = (int) (totalMs/durTimeMs);
			if(durTimeMs*count<totalMs) {
				count = count+1;
			}
			if(count>pageSize) {
				count = pageSize;//数据条数超过页长，那就分页取了
			}
			List<Map<String, Object>> result = dfc.getDevicesDataListByTimestampMultiThread(deviceColNamesArray, startTimestamp, durTimeMs, count, dataTimestampFormat);
			return result;
		} catch (Exception e) {
			throw new RestException(e.getMessage());
		}
	}

	/**
	 * @param startTimeStr
	 * @param endTimeStr
	 * @param durTimeMs
	 * @param pageNum
	 * @param pageSize
	 * @param deviceColNamesArray
	 * @param dataTimestampFormat
	 * @return
	 * {
	 * 		"timestamp":[123123123],
	 * 		"device:col1":[1],
	 * 		"device:col2":[1]
	 * }
	 */
	public static void main(String[] args) {
		System.out.println(Integer.MAX_VALUE/1000);
	}
	
	@Request(path = "list_value_by_time_zone2",method = RequestMethod.GET,name = "根据时间范围，拉取报表",desc = "返回时间轴结构数据，纵表结构，有限制一次最多10000条")
	public DeviceDataListByTimestamp2 list_value_by_time_zone2(
			@RequestParam(name = "startTimestamp",required = true,desc = "开始时间戳")Long startTimestamp,
			@RequestParam(name = "durTimeMs",required = true,desc = "时间间隔粒度，单位毫秒")Long durTimeMs,
			@RequestParam(name = "count",required = true,desc = "查询条数，最大10000，实际返回结果可能不足")Integer count,
			@RequestParam(name = "forceCount",required = false,desc = "强制实际返回count条，无数据的null，默认不强制")Boolean forceCount,
			@RequestParam(name = "deviceColNamesArray",required = true,desc = "[\"设备1:列1,列2\",\"设备2:列1,列2\"]")String[] deviceColNamesArray,
			@RequestParam(name = "dataTimestampFormat",required = false,desc = "返回数据的时间戳格式化，默认不带格式化")String dataTimestampFormat
			){
		try {
			if(durTimeMs < 1) {
				durTimeMs = 1l;//最少1毫秒
			}
			if(count < 1) {
				count = 1;
			}
			if(count < 1) {
				count = 1;
			}
			if(count > 10000) {
				throw new Exception("number > 10000");
			}
			
			//计算真正的count,因为当你要到最近的数据，实际可能不足count条，如果按count条要，就会都是空的
			if(forceCount == null || !forceCount) {
				//默认不强制
				Set<String> devicePathSet = new HashSet<>();
				for (String deviceColNames : deviceColNamesArray) {
					String[] split = deviceColNames.split(":");
					if (split.length != 2) {
						throw new LogableException("参数[deviceColNamesArray]的值格式错误：{}", deviceColNames);
					}
					
					devicePathSet.add(split[0]);
				}
				String[] devicePathAry = devicePathSet.toArray(new String[0]);
				Map<String, DataRecordPackage> get_last_record_batch = get_last_record_batch(devicePathAry, null, null, null, null);
				
				//这个endTime是所有设备数据的最晚endTime
				Long endTimestamp = null;
				for(int i=0;i<devicePathAry.length;i++) {
					DataRecordPackage lastRecord = get_last_record_batch.get(devicePathAry[i]);
					if(lastRecord == null) {
						continue;
					}
					long lastRecordTimestamp = ((DataRecordPlus)lastRecord.getRecord()).getTimestamp();
					if(endTimestamp == null) {
						endTimestamp = lastRecordTimestamp;
					}else {
						if(endTimestamp < lastRecordTimestamp) {
							//取更晚的时间
							endTimestamp = lastRecordTimestamp;
						}
					}
				}
				
				//根据endTime来计算真正的count
				if(endTimestamp == null) {
					//说明都没有数据
					count = 0;
				}else {
					if(endTimestamp >= startTimestamp) {
						long totalMs = (endTimestamp-startTimestamp)+1;
						int countByTimestamp = (int) (totalMs/durTimeMs);
						if(durTimeMs*countByTimestamp<totalMs) {
							countByTimestamp = countByTimestamp+1;
						}
						if(countByTimestamp < count) {
							//以小的为准，超过的不算，说明要count条还有的赋予，说明后面数据还有时间
							count = countByTimestamp;
						}
					}else {
						count = 0;
					}
				}
				
			}
			
			DeviceDataListByTimestamp2 result = dfc.getDevicesDataListByTimestamp2MultiThread(deviceColNamesArray, startTimestamp, durTimeMs, count, dataTimestampFormat);
			return result;
		} catch (Exception e) {
			LogUtil.error(e);
			throw new RestException(e.getMessage());
		}
	}

	@Request(path = "page_search",method = RequestMethod.GET,name = "查询记录")
	public Page page_search(
			@RequestParam(name = "devicePath",required = true,desc = "设备路径，一般是：系统/设备id")String devicePath,
			@RequestParam(name = "dataYear",required = true,desc = "数据年份（四位数字）",compile = "^[0-9]{4}$")String dataYear,
			@RequestParam(name = "dataMonth",required = true,desc = "数据月份（两位数字）",compile = "^[0-9]{2}$")String dataMonth,
			@RequestParam(name = "dataDay",required = true,desc = "数据日（两位数字）",compile = "^[0-9]{2}$")String dataDay,
			@RequestParam(name = "dataBlockIndex",required = true,desc = "查询数据块序号，从1开始")Integer dataBlockIndex,
			@RequestParam(name = "pageNum",required = true,desc = "页码，从1开始")Integer pageNum,
			@RequestParam(name = "pageSize",required = true,desc = "页长")Integer pageSize
			){
		try {
			return dfc.pageSearch(devicePath, dataYear, dataMonth, dataDay, dataBlockIndex, pageNum, pageSize);
		} catch (Exception e) {
			throw new RestException("执行失败，{}", e.getMessage());
		}
	}

	@Request(path = "get_last_record",method = RequestMethod.GET,name = "查询最后一条记录")
	public DataRecordPackage get_last_record(
			@RequestParam(name = "devicePath",required = true,desc = "设备路径，一般是：系统/设备id")String devicePath,
			@RequestParam(name = "dataYear",required = false,desc = "数据年份（四位数字）",compile = "^[0-9]{4}$")String dataYear,
			@RequestParam(name = "dataMonth",required = false,desc = "数据月份（两位数字）",compile = "^[0-9]{2}$")String dataMonth,
			@RequestParam(name = "dataDay",required = false,desc = "数据日（两位数字）",compile = "^[0-9]{2}$")String dataDay,
			@RequestParam(name = "dataBlockIndex",required = false,desc = "查询数据块序号，从1开始")Integer dataBlockIndex
			){
		try {
			return dfc.getLastRecord(devicePath, dataYear, dataMonth, dataDay, dataBlockIndex);
		} catch (Exception e) {
			throw new RestException(e.getMessage());
		}
	}
	
	@Request(path = "get_last_record_batch",method = RequestMethod.GET,name = "批量查询最后一条记录")
	public Map<String,DataRecordPackage> get_last_record_batch(
			@RequestParam(name = "devicePathAry",required = true,desc = "设备路径数组，一般是：系统/设备id")String[] devicePathAry,
			@RequestParam(name = "dataYear",required = false,desc = "数据年份（四位数字）",compile = "^[0-9]{4}$")String dataYear,
			@RequestParam(name = "dataMonth",required = false,desc = "数据月份（两位数字）",compile = "^[0-9]{2}$")String dataMonth,
			@RequestParam(name = "dataDay",required = false,desc = "数据日（两位数字）",compile = "^[0-9]{2}$")String dataDay,
			@RequestParam(name = "dataBlockIndex",required = false,desc = "查询数据块序号，从1开始")Integer dataBlockIndex
			){
		Map<String,DataRecordPackage> result = new HashMap<>();
		for(String devicePath:devicePathAry) {
			try {
				DataRecordPackage lastRecord = get_last_record(devicePath, dataYear, dataMonth, dataDay, dataBlockIndex);
				if(lastRecord != null) {
					result.put(devicePath, lastRecord);
				}
			} catch (Exception e) {}
		}
		return result;
	}

	@Request(path = "get_data_block_info",method = RequestMethod.GET,name = "查询数据块的块信息")
	public DataBlockInfoPlus get_data_block_info(
			@RequestParam(name = "devicePath",required = true,desc = "设备路径，一般是：系统/设备id")String devicePath,
			@RequestParam(name = "dataYear",required = true,desc = "数据年份（四位数字）",compile = "^[0-9]{4}$")String dataYear,
			@RequestParam(name = "dataMonth",required = true,desc = "数据月份（两位数字）",compile = "^[0-9]{2}$")String dataMonth,
			@RequestParam(name = "dataDay",required = true,desc = "数据日（两位数字）",compile = "^[0-9]{2}$")String dataDay,
			@RequestParam(name = "dataBlockIndex",required = true,desc = "查询数据块序号，从1开始")Integer dataBlockIndex
			){
		try {
			return dfc.getDataBlockInfo(devicePath, dataYear, dataMonth, dataDay, dataBlockIndex);
		} catch (Exception e) {
			throw new RestException("执行失败，{}", e.getMessage());
		}
	}

	@Request(path = "add",method = RequestMethod.POST,name = "保存记录")
	public void add(
			@RequestParam(name = "devicePath",required = true,desc = "设备路径，一般是：系统/设备id",compile = "^[a-zA-Z0-9/]+$")String devicePath,
			@RequestParam(name = "colNames",required = true,desc = "列名，以英文逗号分隔")String colNames,
			@RequestParam(name = "colValues",required = true,desc = "值，以英文逗号分隔")String colValues,
			@RequestParam(name = "timestamp",required = false,desc = "数据时间戳，可以指定，否则默认当前系统时间")Long timestamp
			){
		Map<String,Object> addData = new HashMap<>();
		addData.put("devicePath", devicePath);
		addData.put("colNames", colNames);
		addData.put("colValues", colValues);
		addData.put("timestamp", timestamp);
		if(!addFlag) {
			LogUtil.info("停止接收新单条数据，deviceId:{}，timestamp:{}",devicePath,timestamp);
			throw new RestException("停止接收新数据");
		}
		lock.lock();
		addDataList.add(addData);
		lock.unlock();
	}

	@Request(path = "add_batch",method = RequestMethod.POST,name = "保存记录")
	public void add_batch(
			@RequestParam(name = "batch",required = true,desc = "批量数据：[{devicePath,colNames,colValues,timestamp}]")String batch
			){
		if(!addFlag) {
			LogUtil.info("停止接收新批量数据");
			throw new RestException("停止接收新数据");
		}
		List<?> fromJson = null;
		try {
			fromJson = JsonUtil.fromJson(batch, List.class);
		} catch (Exception e) {
			throw new RestException(e.getMessage());
		}
		lock.lock();
		try {
			for(Object obj:fromJson) {
				Map<?,?> data = (Map<?,?>)obj;
				addDataList.add(data);
			}
		} catch (Exception e) {
			throw new RestException(e.getMessage());
		} finally {
			lock.unlock();
		}
	}

	//如果addFlag=false，停止接收新数据
	private volatile boolean addFlag = true;
	@Request(path = "get_add_flag",method = RequestMethod.GET,name = "获取数据接收开关状态",desc = "=false只代表不接收新数据了，不代表之前数据都已经持久化完毕，必须配合get_add_data_list_size、get_cached_io_list_size接口同时确认")
	public boolean get_add_flag() {
		return addFlag;
	}
	
	private Lock lock = new ReentrantLock();
	//消息的缓存队列
	private List<Map<?,?>> addDataList = new ArrayList<>();
	
	@Request(path = "get_add_data_list_size",method = RequestMethod.GET,name = "查看添加数据队列长度",desc = "如果=0并且add_flag=false，说明所有数据都已经处理完，但是不一定持久化到磁盘，要看io是不是全部关闭了")
	public int get_add_data_list_size(){
		int size = 0;
		lock.lock();
		size =  addDataList.size();
		lock.unlock();
		return size;
	}

	@Request(path = "get_cached_io_list_size",method = RequestMethod.GET,name = "查看系统开启的缓存io数量",desc = "如果=0并且add_flag=false，说明所有缓存io都已经关闭")
	public int get_cached_io_list_size(){
		return dfc.getIoSize();
	}

	@Request(path = "stop_add_data",method = RequestMethod.GET,name = "停止接收新数据")
	public void stop_add_data(){
		addFlag = false;
	}

	@Request(path = "stop_add_data_and_system",method = RequestMethod.GET,name = "停止接收新数据，并等待数据队列处理完后停止服务")
	public String stop_add_data_and_system(){
		addFlag = false;
		int dataSize = get_add_data_list_size();
		while(dataSize > 0) {
			LogUtil.info("新数据队列尚未保存结束，剩余：{}条",dataSize);
			try {
				Thread.sleep(1000);
			} catch (Exception e) {}
			dataSize = get_add_data_list_size();
		}
		
		int ioSize = dfc.getIoSize();
		while(ioSize > 0) {
			LogUtil.info("数据未完全同步到磁盘，剩余io未关闭：{}个",ioSize);
			try {
				Thread.sleep(1000);
			} catch (Exception e) {}
			ioSize = dfc.getIoSize();
		}
		
		new Thread("stop_add_data_and_system_thread") {
			public void run() {
				try {
					Thread.sleep(1000);
				} catch (Exception e) {}
				LogUtil.info("新数据队列保存结束，现在停止服务");
				System.exit(0);
			};
		}.start();
		return "新数据队列保存结束，1s后停止服务";
	}

	@Value("${server.readonly}")
	private Boolean readonly;
	
	@Override
	public double timeSec() {
		if(readonly) {
			addFlag = false;
			LogUtil.info("Readonly, Api接收新数据停止！");
			stopAsync();
			LogUtil.info("Readonly, Api数据持久化停止！");
			return Integer.MAX_VALUE;
		}
		return 0;
	}
	
	@Override
	public void doSomething() {
		if(readonly) {
			stopAsync();
			LogUtil.info("Readonly, Api数据持久化停止！");
		}
		List<Map<?,?>> tmpList = new ArrayList<>();
		lock.lock();
		try {
			tmpList.addAll(addDataList);
			addDataList.clear();
		} finally {
			lock.unlock();
		}
		
		if(CollectionUtil.isEmpty(tmpList)) {
			return;
		}

//		long startTime = System.currentTimeMillis();
		for(Map<?,?> data:tmpList) {
			try {
				String devicePath = data.get("devicePath").toString();
				String colNames = data.get("colNames").toString();
				String colValues = data.get("colValues").toString();
				Long timestamp = (Long)data.get("timestamp");
				dfc.addData(devicePath, colNames, colValues, timestamp);
			} catch (Exception e) {
				LogUtil.error(e);
				LogUtil.error("idls入库失败：{}",JsonUtil.toJson(data));
			}
		}
//		LogUtil.info("add data size:{},cost {}ms",tmpList.size(),System.currentTimeMillis()-startTime);
//		startTime = System.currentTimeMillis();
	}
	
}
